package com.rlyy.grpc.strategy.single;

import com.alibaba.fastjson.JSONObject;
import com.rlyy.common.enums.CommonEnum;
import com.rlyy.common.exception.BaseRuntimeException;
import com.rlyy.common.argument.ParameterParsing;
import com.rlyy.common.exception.resolvers.MyExceptionHandlerExceptionResolver;
import com.rlyy.common.request.FullRequest;
import com.rlyy.common.request.GrpcRequestContextHolder;
import com.rlyy.common.response.ResponseResult;
import com.rlyy.common.response.ResultUtil;
import com.rlyy.grpc.server.GrpcHandlerFacade;
import com.rlyy.grpc.component.RpcRequest;
import com.rlyy.grpc.component.RpcResponse;
import com.rlyy.mapping.ReflexEntity;
import com.rlyy.mapping.RequestHandlerMapping;
import io.grpc.stub.StreamObserver;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.Optional;

/**
 * 处理单次grpc请求
 *
 * @author yuanqinglong
 * @since 2020/11/11 9:34
 */
@Component
public class PlatformRequestProcess implements ISingleRequestProcess {

    static final Logger logger = LoggerFactory.getLogger(PlatformRequestProcess.class);

    /**
     * url和mapping映射关系
     */
    @Autowired
    private RequestHandlerMapping requestHandlerMapping;

    /**
     * 调用方配置的请求方式、不配置Servlet默认处理GET请求
     */
    //@Autowired
    private RequestProcessingConditions requestMethodConfiguration;

    @Autowired
    private ParameterParsing parameterParsing;

    @Autowired
    private MyExceptionHandlerExceptionResolver exceptionResolver;


    @Override
    public boolean supportsHandler(RpcRequest request) {
        return true;
    }


    @Override
    public void requestHandler(RpcRequest request, StreamObserver<RpcResponse> responseObserver) {
        this.finalHandlerProcess(request, responseObserver);
    }


    /**
     * 处理grpc请求
     *
     * @param request          请求参数
     * @param responseObserver 返回
     */
    public void finalHandlerProcess(RpcRequest request, StreamObserver<RpcResponse> responseObserver) {
        // 校验参数
        if (StringUtils.isEmpty(request.getData())) {
            GrpcHandlerFacade.responseError(responseObserver, CommonEnum.PARAMS_NOT_ENOUGH);
            return;
        }
        ResponseResult result = null;
        FullRequest fullRequest = null;
        try {
            // 转换请求对象
            fullRequest = JSONObject.parseObject(request.getData(), FullRequest.class);
            GrpcRequestContextHolder.setRequestAttributes(fullRequest);
            MDC.put("tid", fullRequest.getTid());
            FullRequest finalFullRequest = fullRequest;
            String api = Optional.ofNullable(Strings.trimToNull(fullRequest.getApi())).orElseGet(() -> RequestHandlerMapping.convertUri(finalFullRequest.getApi()));
            if (!requestHandlerMapping.containsUrlMapping(api)) {
                GrpcHandlerFacade.responseError(responseObserver, CommonEnum.NOT_FOUND_API);
                return;
            }
            ReflexEntity reflexEntity = requestHandlerMapping.getMapping(api);
            requestHandlerMapping.apiIncrementAndGet(api);
            // 根据执行目标方法参数类型解析封装
            Object[] args = parameterParsing.parsePackageParameters(reflexEntity.getMethod(), fullRequest);
            result = (ResponseResult) reflexEntity.getMethod().invoke(reflexEntity.getInstance(), args);
        } catch (Exception e) {
            Throwable throwable = e instanceof InvocationTargetException ? ((InvocationTargetException) e).getTargetException() : e;
            // 调用invoke执行目标方法 统一处理目标方法抛出的异常和记录日志
            logger.error("has an error:{}, param:{} ", throwable, fullRequest);
            result = exceptionResolver.invokeExceptionMethod(throwable);
        } finally {
            GrpcRequestContextHolder.resetRequestAttributes();
            // 返回结果
            this.returnResult(fullRequest, result, responseObserver);
        }
    }


    /**
     * 返回处理结果
     *
     * @param formatFullRequest 请求信息
     * @param result            返回结果
     * @param responseObserver  .
     */
    private void returnResult(FullRequest formatFullRequest, ResponseResult result, StreamObserver<RpcResponse> responseObserver) {
        if (formatFullRequest == null) {
            this.returnResult(result, responseObserver);
            return;
        }
        requestHandlerMapping.apiDecrementAndGet(formatFullRequest.getApi());
        //请求结束 清除此线程的请求上下文 释放ThreadLocal
        MDC.remove(formatFullRequest.getTid());
        result = Optional.ofNullable(result).orElseGet(() -> ResultUtil.error(CommonEnum.SERVER_IS_BUSY));
        result.setTid(formatFullRequest.getTid());
        //返回结果
        GrpcHandlerFacade.responseSuccess(responseObserver, result);
    }

    /**
     * 返回处理结果
     *
     * @param result           返回结果
     * @param responseObserver .
     */
    private void returnResult(ResponseResult result, StreamObserver<RpcResponse> responseObserver) {
        result = Optional.ofNullable(result).orElseGet(() -> ResultUtil.error(CommonEnum.PARAMS_NOT_ENOUGH));
        //返回结果
        GrpcHandlerFacade.responseSuccess(responseObserver, result);
    }

    /**
     * 包装异常信息
     *
     * @param e           异常信息
     * @param fullRequest 请求参数
     * @return 返回包装异常信息
     */
    public ResponseResult invocationExceptionHandler(InvocationTargetException e, FullRequest fullRequest) {
        Throwable targetEx = e.getTargetException();
        targetEx.printStackTrace();
        // 手动抛出的异常为非系统异常 包装自定义错误信息返回页面
        if (targetEx instanceof BaseRuntimeException) {
            return ResultUtil.error((BaseRuntimeException) targetEx);
        } else if (targetEx instanceof SQLException) {
            logger.error("SQL error: {} param:{}", targetEx, fullRequest);
        } else {
            // 非手动抛出的异常为系统异常 如：NPE 需要对代码进行处理
            logger.error("has an error:{}, param:{} ", targetEx, fullRequest);
        }
        return ResultUtil.error(CommonEnum.SERVER_IS_BUSY);
    }

}
